﻿/*
* 创建者：LIYINFAI
* 创建时间：2023/10/24 9:35:26
* 描述：
* --------------------------------------
* 修改说明：
* --------------------------------------
*/
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;

namespace CommonLibrary
{
    /// <summary>
    /// 报警触发
    /// </summary>
    public class AlarmEntity
    {
        /// <summary>
        /// 报警配置（报警代码，报警内容，PLC地址等）
        /// </summary>
        private static string CONFIG_FILE_PATH = Path.Combine(CommonHelper.COMMON_CONFIG_DIR, "AlarmSetting.json");
        /// <summary>
        /// 报警代码
        /// </summary>
        [JsonProperty(PropertyName = "Code")]
        [Description("报警代码")]
        public string Code { get; set; }
        /// <summary>
        /// 报警内容
        /// </summary>
        [JsonProperty(PropertyName = "Name")]
        [Description("报警内容")]
        public string Name { get; set; }
        /// <summary>
        /// PLC地址
        /// </summary>
        [JsonProperty(PropertyName = "PLCAddress")]
        [Description("PLC地址")]
        public string PLCAddress { get; set; }
        /// <summary>
        /// 数据类型
        /// </summary>
        [JsonProperty(PropertyName = "DataType")]
        [Description("数据类型")]
        public EDataType DataType { get; set; } = EDataType.SHORT;
        /// <summary>
        /// 触发值
        /// </summary>
        [JsonProperty(PropertyName = "StartValue")]
        [Description("触发值")]
        public object StartValue { get; set; } = true;
        /// <summary>
        /// 复位值
        /// </summary>
        [JsonProperty(PropertyName = "EndValue")]
        [Description("复位值")]
        public object EndValue { get; set; } = false;
        /// <summary>
        /// 获取所有报警触发设置
        /// </summary>
        /// <returns></returns>
        public static List<AlarmEntity> GetAlarmConfig()
        {
            return CommonHelper.GetJsonListFromFile<AlarmEntity>(CONFIG_FILE_PATH);
        }
        /// <summary>
        /// 保存所有报警触发设置
        /// </summary>
        /// <param name="list"></param>
        public static void SaveAlarmConfig(List<AlarmEntity> list)
        {
            CommonHelper.SaveToJsonFile(list, CONFIG_FILE_PATH);
        }
        /// <summary>
        /// 是否属于正常数据
        /// </summary>
        /// <returns></returns>
        public bool IsValid()
        {
            return !string.IsNullOrWhiteSpace(this.Code) && !string.IsNullOrWhiteSpace(this.Name) && !string.IsNullOrWhiteSpace(this.PLCAddress);
        }
    }
    /// <summary>
    /// 报警开始事件处理代理
    /// </summary>
    /// <param name="code"></param>
    /// <param name="name"></param>
    /// <param name="startTime"></param>
    public delegate void HandleStartAlarmDelegate(string code, string name, DateTime startTime);
    /// <summary>
    /// 报警结束事件处理代理
    /// </summary>
    /// <param name="code"></param>
    /// <param name="name"></param>
    /// <param name="startTime"></param>
    /// <param name="endTime"></param>
    public delegate void HandleAlarmDelegate(string code, string name, DateTime startTime, DateTime endTime);
    /// <summary>
    /// 报警触发帮助类，用于收集报警内容，报警开始时间以及结束时间，根据需要进行处理（比如上传到MES或者写入文件等，可自定义）
    /// </summary>
    public class AlarmHelper
    {
        /// <summary>
        /// 默认线程休眠时间 100ms
        /// </summary>
        public int SleepTimes { get; set; } = 1000;
        /// <summary>
        /// 处理线程
        /// </summary>
        private Thread mRunThread = null;
        /// <summary>
        /// 定制化处理事件（报警开始）
        /// </summary>
        public event HandleStartAlarmDelegate HandleStartAlarm = null;
        /// <summary>
        /// 定制化处理事件（报警结束）
        /// </summary>
        public event HandleAlarmDelegate HandleAlarm = null;
        /// <summary>
        /// PLC地址内容读取对象
        /// </summary>
        private List<PLCTagSetting> mTags = new List<PLCTagSetting>();
        /// <summary>
        /// 报警项目定义
        /// </summary>
        private List<AlarmEntity> mAlarms = new List<AlarmEntity>();
        /// <summary>
        /// 针对 mAlarms 的锁，以防止更新与读取时产生错误
        /// </summary>
        private object ALARMS_LOCK = new object();
        /// <summary>
        /// 报警详情
        /// </summary>
        private Dictionary<string, DateTime> mTriggerTimes = new Dictionary<string, DateTime>();
        /// <summary>
        /// PLC通讯类型
        /// </summary>
        private EPLCOptType mEPCOptType = EPLCOptType.FINS;
        /// <summary>
        /// AlarmHelper初始化
        /// </summary>
        /// <param name="handleAlarmEvent"></param>
        /// <param name="handleStartAlarmEvent"></param>
        public static void Init(HandleAlarmDelegate handleAlarmEvent = null, HandleStartAlarmDelegate handleStartAlarmEvent = null)
        {
            lock (LOCK_OBJ)
            {
                if (alarmHelper == null)
                {
                    var alarms = AlarmEntity.GetAlarmConfig();
                    UpdateAlarmDataType(alarms);
                    alarmHelper = new AlarmHelper(alarms, handleAlarmEvent, handleStartAlarmEvent);
                    alarmHelper.Start();
                }
            }
        }
        private static void UpdateAlarmDataType(List<AlarmEntity> alarms)
        {
            if (alarms != null && alarms.Count > 0)
            {
                foreach (var alarm in alarms)
                {
                    try
                    {
                        switch (alarm.DataType)
                        {
                            case EDataType.SHORT: alarm.StartValue = short.Parse(alarm.StartValue.ToString()); alarm.EndValue = short.Parse(alarm.EndValue.ToString()); break;
                            case EDataType.INT: alarm.StartValue = short.Parse(alarm.StartValue.ToString()); alarm.EndValue = short.Parse(alarm.EndValue.ToString()); break;
                            case EDataType.USHORT: alarm.StartValue = short.Parse(alarm.StartValue.ToString()); alarm.EndValue = short.Parse(alarm.EndValue.ToString()); break;
                            case EDataType.UINT: alarm.StartValue = short.Parse(alarm.StartValue.ToString()); alarm.EndValue = short.Parse(alarm.EndValue.ToString()); break;
                            case EDataType.LONG: alarm.StartValue = short.Parse(alarm.StartValue.ToString()); alarm.EndValue = short.Parse(alarm.EndValue.ToString()); break;
                            case EDataType.ULONG: alarm.StartValue = short.Parse(alarm.StartValue.ToString()); alarm.EndValue = short.Parse(alarm.EndValue.ToString()); break;
                        }
                    }
                    catch (Exception ex)
                    {
                        LogHelper.Error(ex);
                    }
                }
            }
        }
        /// <summary>
        /// 锁
        /// </summary>
        private static object LOCK_OBJ = new object();
        /// <summary>
        /// 单例对象
        /// </summary>
        private static AlarmHelper alarmHelper = null;
        /// <summary>
        /// 单例对象
        /// </summary>
        public static AlarmHelper ALARM_HELPER
        {
            get
            {
                return alarmHelper;
            }
        }
        /// <summary>
        /// 获取所有报警配置
        /// </summary>
        /// <returns></returns>
        public List<AlarmEntity> GetAlarms()
        {
            return this.mAlarms;
        }
        /// <summary>
        /// 初始化一个帮助类
        /// </summary>
        private AlarmHelper(List<AlarmEntity> alarms, HandleAlarmDelegate handleAlarmEvent = null, HandleStartAlarmDelegate handleStartAlarmEvent = null)
        {
            this.UpdateAlarms(alarms);
            this.mEPCOptType = PLCHelper.GetPLCConfig().OptType;
            this.HandleAlarm = handleAlarmEvent;
            this.HandleStartAlarm = handleStartAlarmEvent;
        }
        /// <summary>
        /// 增加某个报警配置
        /// </summary>
        /// <param name="alarm"></param>
        /// <returns></returns>
        public bool AddAlarm(AlarmEntity alarm)
        {
            lock (ALARMS_LOCK)
            {
                bool result = false;
                if (alarm != null && alarm.IsValid() && this.mAlarms != null && this.mAlarms.Find(v => v.Code.Equals(alarm.Code)) == null)
                {
                    this.mAlarms.Add(alarm);
                    var list = this.mAlarms.ToList();
                    UpdateAlarms(list);
                    result = true;
                    LogHelper.Debug($"【{UserHelper.LoginUser}】新增报警记录：{alarm}，结果：{result}");
                }
                return result;
            }
        }
        /// <summary>
        /// 编辑某个报警配置
        /// </summary>
        /// <param name="alarm"></param>
        /// <returns></returns>
        public bool EditAlarm(AlarmEntity alarm)
        {
            lock (ALARMS_LOCK)
            {
                bool result = false;
                if (alarm != null && alarm.IsValid() && this.mAlarms != null && this.mAlarms.Find(v => v.Code.Equals(alarm.Code)) != null)
                {
                    var findItem = this.mAlarms.Find(v => v.Code.Equals(alarm.Code));
                    findItem.PLCAddress = alarm.PLCAddress;
                    var list = this.mAlarms.ToList();
                    UpdateAlarms(list);
                    result = true;
                    LogHelper.Debug($"【{UserHelper.LoginUser}】编辑报警记录，修改前：{findItem}，修改后：{alarm}，结果：{result}");
                }
                return result;
            }
        }
        /// <summary>
        /// 删除某个报警配置
        /// </summary>
        /// <param name="alarm"></param>
        /// <returns></returns>
        public bool DeleteAlarm(AlarmEntity alarm)
        {
            lock (ALARMS_LOCK)
            {
                bool result = false;
                if (alarm != null && alarm.IsValid() && this.mAlarms != null && this.mAlarms.Find(v => v.Code.Equals(alarm.Code)) != null)
                {
                    var findItem = this.mAlarms.Find(v => v.Code.Equals(alarm.Code));
                    this.mAlarms.Remove(findItem);
                    var list = this.mAlarms.ToList();
                    UpdateAlarms(list);
                    result = true;
                    LogHelper.Debug($"【{UserHelper.LoginUser}】删除报警记录：{alarm}，结果：{result}");
                }
                return result;
            }
        }
        /// <summary>
        /// 更新报警配置内容
        /// </summary>
        /// <param name="alarms"></param>
        public void UpdateAlarms(List<AlarmEntity> alarms)
        {
            lock (ALARMS_LOCK)
            {
                UpdateAlarmDataType(alarms);
                this.mTags.Clear();
                this.mAlarms.Clear();
                if (alarms != null && alarms.Count > 0)
                {
                    foreach (var a in alarms)
                    {
                        if (!string.IsNullOrWhiteSpace(a.Code) && !string.IsNullOrWhiteSpace(a.PLCAddress))
                        {
                            this.mTags.Add(new PLCTagSetting()
                            {
                                Tag = a.PLCAddress,
                                ORMName = a.Code,
                                TypeName = a.DataType.ToString().ToLower(),
                            });
                        }
                    }
                    this.mAlarms = alarms;
                }
                AlarmEntity.SaveAlarmConfig(alarms);
            }
        }
        /// <summary>
        /// 析构函数
        /// </summary>
        ~AlarmHelper()
        {
            Stop();
        }
        /// <summary>
        /// 启动
        /// </summary>
        private void Start()
        {
            Stop();
            this.mRunThread = new Thread(() =>
            {
                this.mTriggerTimes.Clear();
                while (GlobalStatus.PROGRAM == EAppStatus.RUN)
                {
                    try
                    {
                        if (APPInstance.IsPLCConnected)
                        {
                            lock (ALARMS_LOCK)
                            {
                                var values = APPInstance.PLC.BatchRead(this.mTags, true);
                                if (values != null && values.Count > 0)
                                {
                                    foreach (var m in values)
                                    {
                                        var alarmEntity = this.mAlarms.Find(v => v.Code.Equals(m.Key));
                                        if (alarmEntity != null)
                                        {
                                            if (m.Value.Equals(alarmEntity.StartValue) && !this.mTriggerTimes.ContainsKey(m.Key))
                                            {
                                                DateTime startTime = DateTime.Now;
                                                this.mTriggerTimes.Add(m.Key, DateTime.Now);
                                                if (this.HandleStartAlarm != null)
                                                {
                                                    this.HandleStartAlarm(alarmEntity.Code, alarmEntity.Name, startTime);
                                                }
                                                LogHelper.Info($"【报警开始】报警代码：{alarmEntity.Code}，报警内容：{alarmEntity.Name}，报警时间：{startTime.ToString("yyyy-MM-dd HH:mm:ss")}");
                                                AlarmsSQLiteDb.StartAlarm(alarmEntity.Code, alarmEntity.Name, startTime.ToString("yyyy-MM-dd HH:mm:ss"));
                                            }
                                            else if (m.Value.Equals(alarmEntity.EndValue) && this.mTriggerTimes.ContainsKey(m.Key))
                                            {
                                                DateTime endTime = DateTime.Now;
                                                if (this.HandleAlarm != null)
                                                {
                                                    this.HandleAlarm(alarmEntity.Code, alarmEntity.Name, this.mTriggerTimes[m.Key], endTime);
                                                }
                                                this.mTriggerTimes.Remove(m.Key);
                                                LogHelper.Info($"【报警结束】报警代码：{alarmEntity.Code}，报警内容：{alarmEntity.Name}，结束时间：{endTime.ToString("yyyy-MM-dd HH:mm:ss")}");
                                                AlarmsSQLiteDb.EndAlarm(alarmEntity.Code, alarmEntity.Name, endTime.ToString("yyyy-MM-dd HH:mm:ss"));
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        LogHelper.Error(ex);
                    }
                    Thread.Sleep(SleepTimes);
                }
            });
            this.mRunThread.Start();
        }
        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            AlarmsSQLiteDb.EndEmptyAlarms();
        }
        /// <summary>
        /// 关闭
        /// </summary>
        public static void Close()
        {
            if (alarmHelper != null)
            {
                alarmHelper.Stop();
            }
        }
    }
}
